#include "MazeGenerator.hpp"


MazeGenerator::MazeGenerator()
{
	srand(time(nullptr));
}

MazeGenerator::~MazeGenerator()
{
}

MazeGenerator& MazeGenerator::getInstance()
{
	static MazeGenerator instance;
	return instance;
}

void MazeGenerator::genMazeRecuresively(const std::shared_ptr<Maze>& result, int px, int py)
{
	int dir[4] = { 0, 1, 2, 3 };
	int loop_times = rand() % 10;
	for (int i = 0; i < loop_times; i++)
	{
		int f = rand() % 4;
		int s = rand() % 4;
		int t = dir[f];
		dir[f] = dir[s];
		dir[s] = t;
	}
	for (auto d : dir)
	{
		int pos_x = px;
		int pos_y = py;
		switch (d)
		{
		case 0:
			px += 1;
			if (checkPoint(result, px, py) && (result->get(px, py) != 0) &&
				checkPoint(result, px + 1, py) && (result->get(px + 1, py) != 0) &&
				checkPoint(result, px, py + 1) && (result->get(px, py + 1) != 0) &&
				checkPoint(result, px, py - 1) && (result->get(px, py - 1) != 0))
			{
				result->get(px, py) = 0;
				genMazeRecuresively(result, px, py);
			}
			break;
		case 1:
			px -= 1;
			if (checkPoint(result, px, py) && (result->get(px, py) != 0) &&
				checkPoint(result, px - 1, py) && (result->get(px - 1, py) != 0) &&
				checkPoint(result, px, py + 1) && (result->get(px, py + 1) != 0) &&
				checkPoint(result, px, py - 1) && (result->get(px, py - 1) != 0))
			{
				result->get(px, py) = 0;
				genMazeRecuresively(result, px, py);
			}
			break;
		case 2:
			py += 1;
			if (checkPoint(result, px, py) && (result->get(px, py) != 0) &&
				checkPoint(result, px, py + 1) && (result->get(px, py + 1) != 0) &&
				checkPoint(result, px + 1, py) && (result->get(px + 1, py) != 0) &&
				checkPoint(result, px - 1, py) && (result->get(px - 1, py) != 0))
			{
				result->get(px, py) = 0;
				genMazeRecuresively(result, px, py);
			}
			break;
		case 3:
			py -= 1;
			if (checkPoint(result, px, py) && (result->get(px, py) != 0) &&
				checkPoint(result, px, py - 1) && (result->get(px, py - 1) != 0) &&
				checkPoint(result, px + 1, py) && (result->get(px + 1, py) != 0) &&
				checkPoint(result, px - 1, py) && (result->get(px - 1, py) != 0))
			{
				result->get(px, py) = 0;
				genMazeRecuresively(result, px, py);
			}
			break;
		default:
			break;
		}
		px = pos_x;
		py = pos_y;
	}
}

std::shared_ptr<Maze> MazeGenerator::genMaze(int dimension,const std::shared_ptr<CollisionWorld>& collision)
//get a new Maze generated by MazeGenerator
{
	std::shared_ptr<Maze> result(new Maze);
	result->width = dimension;
	result->height = dimension;
	result->m_map.reset(new bool[dimension * dimension]);
	for (int i = 0; i<dimension * dimension; i++)
	{
		result->m_map[i] = true;
	}

	result->get(dimension / 2, dimension / 2) = 0;
	genMazeRecuresively(result, dimension / 2, dimension / 2);


	//register maze Blocks in collision world
	float xOffSet = Maze::blockSize * result->width / 2.0f;
	float yOffSet = Maze::blockSize * result->height / 2.0f;

	for (int i = 0; i < result->height; i++)
	{
		for (int j = 0; j < result->width; j++)
		{
			if (result->get(i, j) == 1)
			{
				collision->addBox(i * Maze::blockSize - xOffSet,
					j * Maze::blockSize - yOffSet,
					Maze::blockSize / 2.0f,
					Maze::blockSize / 2.0f);
			}
		}
	}
	return result;
}

bool MazeGenerator::checkPoint(const std::shared_ptr<Maze>& ref, const std::pair<int, int>& pt)
{
	if (pt.first >= 0 && pt.second >= 0)
	{
		if (pt.first < ref->width && (pt.second < ref->height))
		{
			return true;
		}
	}
	return false;
}


bool MazeGenerator::checkPoint(const std::shared_ptr<Maze>& ref, int a, int b)
{
	if (a >= 0 && b >= 0)
	{
		if (a < ref->width && (b < ref->height))
		{
			return true;
		}
	}
	return false;
}
